4 // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
6 // The use and distribution terms for this software are contained in the file
7 // named license.txt, which can be found in the root of this distribution.
8 // By using this software in any fashion, you are agreeing to be bound by the
9 // terms of this license.
11 // You must not remove this notice, or any other, from this software.
15 // IFEXPR.C -- routines to handle directives
18 // Module contains routines to handle !directives. This module is transparent to
19 // rest of NMAKE. It also contains lgetc() used by lexer.c
26 // function prototypes
28 void skipToNextDirective(void);
29 void processIfs(char*, UCHAR
);
30 UCHAR
ifsPresent(char*, unsigned, char**);
31 void processCmdSwitches(char*);
32 char * readInOneLine(void);
33 char * getDirType(char*, UCHAR
*);
35 // macros that deal w/ the if/else directives' stack
37 #define ifStkTop() (ifStack[ifTop])
38 #define popIfStk() ifTop--
39 #define pushIfStk(A) (ifStack[++ifTop] = A)
42 #define CMDSWITCHES 0x0A
50 // GetTxtChr : get the next character from a text file stream
52 // This routine handles mixed DBCS and ASCII characters as
55 // 1. The second byte of a DBCS character is returned in a
56 // word with the high byte set to the lead byte of the character.
57 // Thus the return value can be used in comparisions with
58 // ASCII constants without being mistakenly matched.
60 // 2. A DBCS space character (0x8140) is returned as two
61 // ASCII spaces (0x20). I.e. return a space the 1st and 2nd
62 // times we're called.
64 // 3. ASCII characters and lead bytes of DBCS characters
65 // are returned in the low byte of a word with the high byte
68 int GetTxtChr(FILE *bs
)
70 extern int chBuf
; // Character buffer
71 int next
; // The next byte
72 int next2
; // The one after that
74 // -1 in chBuf means it doesn't contain a valid character
76 // If we're not in the middle of a double-byte character,
77 // get the next byte and process it.
81 // If this byte is a lead byte, get the following byte
82 // and store both as a word in chBuf.
84 if (_ismbblead(next
)) {
86 chBuf
= (next
<< 8) | next2
;
87 // If the pair matches a DBCS space, set the return value
94 // Else we're in the middle of a double-byte character.
97 // If this is the 2nd byte of a DBCS space, set the return
98 // value to ASCII space.
102 // Else set the return value to the whole DBCS character
106 // Reset the character buffer
110 // Return the next character
129 UngetTxtChr(int c
, FILE *bs
)
131 extern int chBuf
; // Character buffer
132 int nTrailByte
; // The trail byte to put back
134 if (-1 == chBuf
) { // We're not in the middle of a DB character
135 if (0 == (c
>> 8)) { // CASE 1: normal character
136 c
= ungetc(c
, bs
); // putback normal char
137 } else { // CASE 2: at trail byte (c=LBTB)
138 chBuf
= c
; // change chBuf is sufficient
140 } else { // CASE 3: at lead byte (c=LB, chBuf=LBTB)
141 nTrailByte
= chBuf
& (int)0xff; // Figure out the trail byte to putback
142 ungetc(nTrailByte
, bs
); // putback trail byte
143 c
= ungetc(c
, bs
); // putback lead byte
151 // lgetc() local getc - handles directives and returns char
153 // arguments: init global boolean value -- TRUE if tools.ini
154 // is the file being parsed
155 // colZero global boolean value -- TRUE if at first column
158 // gets a character from the currently open file.
160 // if it is column zero and the char is '!' or
161 // there is a previous directive to be processed do
162 // read in one line into buffer.
163 // find directive type and get a pointer to rest of
165 // case directive of:
167 // CMDSWITCHES : set/reset global flags
168 // ERROR : set up global error message
169 // printed by error routine on
170 // termination. (not implemented yet )
171 // INCLUDE : calls processInclude
172 // continues with new file...
173 // UNDEF : undef the macro in the table
178 // ENDIF : change the state information
180 // evaluate expression if required
181 // skip text if required (and look
182 // for the next directive)
183 // ( look at processIfs() )
184 // free extra buffers used (only one buffer need be
186 // increment lexer's line count
187 // we 're now back at column zero
188 // get next char from current file
193 // returns : a character (that is not part of any directive...)
195 // modifies: ifStack if directives' stack, static to this module
196 // ifTop index of current element at top of stack
197 // line lexer's line count...
199 // file current file, if !include is found...
200 // fName if !include is processed...
210 for (c
= GetTxtChr(file
); prevDirPtr
|| (colZero
&& (c
== '!'));
211 ++line
, c
= GetTxtChr(file
)) {
212 colZero
= FALSE
; // we saw a '!' incolZero
214 s
= readInOneLine(); // might modify lbufPtr -
215 // if input text causes realloc */
217 UngetTxtChr(c
, file
);
222 t
= getDirType(s
, &dirType
);
224 if (dirType
== INCLUDE
) {
226 makeError(line
, SYNTAX_UNEXPECTED_TOKEN
, s
);
229 // processInclude eats up first char in new file
230 // if it is space char. we check for that and break out.
232 if (processIncludeFile(t
) == (UCHAR
) NEWLINESPACE
) {
233 c
= ' '; // space character is returned
234 break; // colZero is now FALSE
237 else if (dirType
== CMDSWITCHES
) {
238 processCmdSwitches(t
);
240 else if (dirType
== ERROR
) {
241 makeError(line
, USER_CONTROLLED
, t
);
243 else if (dirType
== MESSAGE
) {
244 if (!_tcsnicmp(t
, "\\t", 2)) {
248 makeMessage(USER_MESSAGE
, t
);
250 else if (dirType
== UNDEF
) {
252 tmp
= _tcstok(t
, " \t");
253 if (_tcstok(NULL
, " \t")) {
254 makeError(line
, SYNTAX_UNEXPECTED_TOKEN
, tmp
);
256 if (NULL
!= (m
= findMacro(tmp
))) {
257 SET(m
->flags
, M_UNDEFINED
);
260 else processIfs(t
, dirType
);
261 colZero
= TRUE
; // finished with this directive
262 if (s
!= lbufPtr
) // free buffer if it had expanded macros
265 return(c
); // return a character to the lexer
271 // arguments: lbufPtr pointer(static/global to this module) to buffer that
272 // will hold text of line being read in
273 // lbufSize size of buffer(static/global to this module), updated
274 // if buffer is realloc'd
275 // actions : skip spaces/tabs and look for the directive.
276 // line continuations allowed in usual way
277 // if space-backslash-nl keep looking...
278 // if colZero of next line has comment char
279 // (#, or ; in tools.ini), look at next line...
280 // if first non-space char is '\n' or EOF report
281 // fatal-error and stop.
283 // keep reading in chars and storing in the buffer until
284 // a newline, EOF or a '#' which is NOT in column
286 // if comment char in column zero ('#' or ';' in tools.ini)
287 // skip the line, continue with text on next line.
288 // if buffer needs to be realloc'd increase size by
289 // MAXBUF, a global constant.
290 // if newline was found, eat up newline.
291 // null terminate string for return.
292 // if '#' was found discard chars till the a newline or EOF.
293 // if EOF was found, push it back on stream for return
294 // to the lexer the next time.
296 // now expand macros. get a different buffer with clean
297 // text after expansion of macros.
299 // modifies : colZero global boolean value ( thru' call to
301 // lbufPtr buffer pointer, in case of reallocs.
302 // lbufSize size of buffer, increased if buffer is realloc'd
303 // Note: the buffer size will grow to be just greater than the size
304 // of the longest directive in any of the files processed,
305 // if it calls for any realloc's
306 // Do NOT process ESCH here. It is processed at a higher level.
308 // returns : pointer to buffer.
318 if (((c
= skipWhiteSpace(FROMSTREAM
)) == '\n') || (c
== EOF
))
319 makeError(line
, SYNTAX_MISSING_DIRECTIVE
);
321 UngetTxtChr(c
, file
);
325 c
= skipBackSlash(c
, FROMSTREAM
);
326 if (c
== '#' || c
== '\n' || c
== EOF
) {
329 if ((index
+2) > lbufSize
) {
332 lbufPtr
= (char *) allocate(lbufSize
+1); // +1 for NULL byte
334 lbufPtr
= (char *) REALLOC(lbufPtr
, lbufSize
+1);
336 makeError(line
, MACRO_TOO_LONG
);
339 *(lbufPtr
+ (index
++)) = (char) c
;
341 *(lbufPtr
+ index
) = '\0'; // null terminate the string
343 for(c
= GetTxtChr(file
); (c
!= '\n') && (c
!= EOF
); c
= GetTxtChr(file
))
345 // newline at end is eaten up
349 UngetTxtChr(c
, file
); // this directive is to be processed
352 s
= lbufPtr
; // start expanding macros here
353 s
= removeMacros(s
); // remove and expand macros in string s
360 // arguments: s - pointer to buffer that has directive text.
361 // dirType - pointer to unsigned char that gets set
362 // with directive type.
364 // actions : goes past directive keyword, sets the type code and
365 // returns a pointer to rest of test.
377 for (t
= s
; *t
&& !WHITESPACE(*t
); ++t
);
378 len
= (int) (t
- s
); // store len of directive
379 while (*t
&& WHITESPACE(*t
)) {
380 ++t
; // go past directive keyword
381 } if (!_tcsnicmp(s
, "INCLUDE", 7) && (len
== 7)) {
383 } else if (!_tcsnicmp(s
, "CMDSWITCHES", 11) && (len
== 11)) {
384 *dirType
= CMDSWITCHES
;
385 } else if (!_tcsnicmp(s
, "ERROR", 5) && (len
== 5)) {
387 } else if (!_tcsnicmp(s
, "MESSAGE", 7) && (len
== 7)) {
389 } else if (!_tcsnicmp(s
, "UNDEF", 5) && (len
== 5)) {
392 *dirType
= ifsPresent(s
, len
, &t
) ; // directive one of "if"s?
396 makeError(line
, SYNTAX_BAD_DIRECTIVE
, lbufPtr
);
402 // processCmdSwitches() -- processes command line switches in makefiles
404 // arguments: t pointer to flag settings specified.
406 // actions : sets or resets global flags as specified in the directive.
407 // The allowed flags are:
408 // s - silent mode, d - debug output (dates printed)
409 // n - no execute mode, i - ignore error returns from commands
410 // u - dump inline files
411 // If parsing tools.ini, can also handle epqtbc
412 // reports a bad directive error for any other flags
415 // modifies : nothing
421 char *t
// pointer to switch values
424 for (; *t
; ++t
) { // ignore errors in flags specified
427 while (*++t
&& *t
!= '-') {
428 if (_tcschr("DINSU", (unsigned short)_totupper(*t
))) {
430 } else if (init
&& _tcschr("ABCEKLPQRTY", (unsigned short)_totupper(*t
))) {
433 makeError(line
, SYNTAX_BAD_CMDSWITCHES
);
442 while (*++t
&& *t
!= '+') {
443 if (_tcschr("DINSU", (unsigned short)_totupper(*t
))) {
445 } else if (init
&& _tcschr("ABCEKLMPQRTV", (unsigned short)_totupper(*t
))) {
448 makeError(line
, SYNTAX_BAD_CMDSWITCHES
);
454 if (!WHITESPACE(*t
)) {
455 makeError(line
, SYNTAX_BAD_CMDSWITCHES
);
465 // ifsPresent() -- checks if current directive is one of the "if"s
467 // arguments: s pointer to buffer with directive name in it
468 // len length of the directive that was seen
469 // t pointer to address upto which processed
471 // actions : does a string compare in the buffer for one of the
472 // directive keywords. If string matches true, it returns
473 // a non-zero value, the code for the specific directive
475 // modifies : nothing
477 // returns : a zero if no match, or the code for directive found.
486 UCHAR ifFlags
= 0; // takes non-zero value when one of
487 // if/else etc is to be processed
489 if (!_tcsnicmp(s
, "IF", 2) && (len
== 2)) {
491 } else if (!_tcsnicmp(s
, "IFDEF", 5) && (len
== 5)) {
492 ifFlags
= IFDEF_TYPE
;
493 } else if (!_tcsnicmp(s
, "IFNDEF", 6) && (len
== 6)) {
494 ifFlags
= IFNDEF_TYPE
;
495 } else if (!_tcsnicmp(s
, "ELSE", 4) && (len
== 4)) {
496 // 'else' or 'else if' or 'else ifdef' or 'else ifndef'
502 for (s
= p
; *p
&& !WHITESPACE(*p
); p
++)
504 len
= (unsigned) (p
- s
);
505 while (*p
&& WHITESPACE(*p
)) {
509 if (!_tcsnicmp(s
, "IF", 2) && (len
== 2)) {
510 ifFlags
= ELSE_IF_TYPE
;
511 } else if (!_tcsnicmp(s
, "IFDEF", 5) && (len
== 5)) {
512 ifFlags
= ELSE_IFDEF_TYPE
;
513 } else if (!_tcsnicmp(s
, "IFNDEF", 6) && (len
== 6)) {
514 ifFlags
= ELSE_IFNDEF_TYPE
;
518 else if (!_tcsnicmp(s
, "ELSEIF", 6) && (len
== 6)) {
519 ifFlags
= ELSE_IF_TYPE
;
521 else if (!_tcsnicmp(s
, "ELSEIFDEF", 9) && (len
== 9)) {
522 ifFlags
= ELSE_IFDEF_TYPE
;
524 else if (!_tcsnicmp(s
, "ELSEIFNDEF", 10) && (len
== 10)) {
525 ifFlags
= ELSE_IFNDEF_TYPE
;
527 else if (!_tcsnicmp(s
, "ENDIF", 5) && (len
== 5)) {
528 ifFlags
= ENDIF_TYPE
;
535 // processIfs() -- sets up / changes state information on "if"s
537 // arguments: s pointer to "if" expression ( don't care
540 // kind code indicating if processing if/else/ifdef etc.
542 // actions : modifies a stack (ifStack) by pushing/popping or
543 // sets/resets bits in the top element on the
544 // stack(examining the previous element pushed if
550 // IF defined() : if no more space on ifStack
551 // (too many nesting levels) abort...
552 // set IFELSE bit in elt.
553 // push elt on ifStack.
554 // if more than one elt on stack
555 // and outer level "ifelse" false
556 // set IGNORE bit, skipToNextDirective
558 // evaluate expression of
560 // if expr true set CONDITION bit in elt
561 // else skipToNextDirective.
562 // ELSE : if no elt on stack or previous
563 // directive was "else", flag error, abort
564 // clear IFELSE bit in elt on stack.
565 // if current ifelse block is to
566 // be skipped (IGNORE bit is on
567 // in outer level if/else),skip...
568 // else FLIP condition bit.
569 // if "else" part is false
570 // skipToNextDirective.
571 // ENDIF : if no elt on stack, flag error,abort
572 // pop an elt from ifStack.
573 // if there are elts on stack
574 // and we are in a "false" block
575 // skipToNextDirective.
578 // modifies: ifStack if directives' stack, static to this module
579 // ifTop index of current element at top of stack
580 // line lexer's line count (thru calls to
581 // skipToNextDirective())
591 UCHAR element
; // has its bits set and is pushed on the ifStack
597 if (ifTop
== IFSTACKSIZE
-1) {
598 makeError(line
, SYNTAX_TOO_MANY_IFS
);
601 SET(element
, NMIFELSE
);
603 if (ifTop
&& OFF(ifStack
[ifTop
-1], NMCONDITION
)) {
604 SET(ifStkTop(), NMIGNORE
);
605 skipToNextDirective();
606 } else if (evalExpr(s
, kind
)) {
607 SET(ifStkTop(), NMCONDITION
);
609 skipToNextDirective();
614 if ((ifTop
< 0) || (OFF(ifStkTop(), NMIFELSE
) && OFF(ifStkTop(), NMELSEIF
))) {
615 makeError(line
, SYNTAX_UNEXPECTED_ELSE
);
617 CLEAR(ifStkTop(), NMIFELSE
);
618 CLEAR(ifStkTop(), NMELSEIF
);
619 if (ON(ifStkTop(), NMIGNORE
)) {
620 skipToNextDirective();
622 FLIP(ifStkTop(), NMCONDITION
);
623 if (OFF(ifStkTop(), NMCONDITION
)) {
624 skipToNextDirective();
630 case ELSE_IFDEF_TYPE
:
631 case ELSE_IFNDEF_TYPE
:
632 if ((ifTop
< 0) || (OFF(ifStkTop(), NMIFELSE
) && OFF(ifStkTop(), NMELSEIF
))) {
633 makeError(line
, SYNTAX_UNEXPECTED_ELSE
);
635 CLEAR(ifStkTop(), NMIFELSE
);
636 SET(ifStkTop(), NMELSEIF
);
637 if (ON(ifStkTop(), NMIGNORE
)) {
638 skipToNextDirective();
640 if (ON(ifStkTop(), NMCONDITION
)) {
641 SET(ifStkTop(), NMIGNORE
);
642 CLEAR(ifStkTop(), NMCONDITION
);
643 skipToNextDirective();
644 } else if (evalExpr(s
, kind
)) {
645 SET(ifStkTop(), NMCONDITION
);
647 skipToNextDirective();
654 makeError(line
, SYNTAX_UNEXPECTED_ENDIF
);
658 if (OFF(ifStkTop(), NMCONDITION
)) {
659 skipToNextDirective();
664 break; // default should never happen
669 // skipToNextDirective() -- skips to next line that has '!' in column zero
671 // actions : gets first char of the line to be skipped if it is
672 // not a directive ( has no '!' on column zero ).
673 // a "line" that is skipped may in fact span many
674 // lines ( by using sp-backslash-nl to continue...)
675 // comments in colZero are skipped as part of the previous
676 // line ('#' or ';' in tools.ini)
677 // comment char '#' elsewhere in line implies the end of
678 // that line (with the next newline / EOF)
679 // if a '!' is found in colZero, read in the next directive
680 // if the directive is NOT one of if/ifdef/ifndef/else/
681 // endif, keep skipping more lines and look for the
682 // next directive ( go to top of the routine here ).
683 // if EOF found before next directive, report error.
685 // modifies : line global lexer line count
690 skipToNextDirective()
697 for (c
= GetTxtChr(file
); (c
!= '!') && (c
!= EOF
) ;c
= GetTxtChr(file
)) {
698 ++line
; // lexer's line count
702 c
= skipBackSlash(c
, FROMSTREAM
);
703 if (c
== '!' && colZero
) {
709 if ((c
== '#') || (c
== '\n') || (c
== EOF
)) {
716 for (c
= GetTxtChr(file
); (c
!= '\n') && (c
!= EOF
); c
= GetTxtChr(file
))
719 if ((c
== EOF
) || (c
== '!')) {
725 if (prevDirPtr
&& (prevDirPtr
!= lbufPtr
)) {
728 prevDirPtr
= readInOneLine();
729 getDirType(prevDirPtr
, &type
);
730 if (type
> ENDIF_TYPE
) { // type is NOT one of the "if"s
734 } else if (c
== EOF
) {
735 makeError(line
, SYNTAX_EOF_NO_DIRECTIVE
);